﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace ALDIOrganizer
{
    public static class Permutator
    {
        static long[] _howTallColumns;

        /// <summary>
        /// size of the bigger set
        /// </summary>
        static int _biggerSetSize;
        /// <summary>
        /// size of the smaller (or equal size) set
        /// </summary>
        static int _smallerSetSize;

        internal static List<int[]> PermutationsChart { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pArray"></param>
        /// <param name="pWhichOne">which of available free lanes</param>
        /// <returns></returns>
        static int GetFreeLaneFromTheLeft(ICollection<int> pArray, int pWhichOne)
        {            
            if (pArray == null ||
                pArray.Count == 0 ||
                pWhichOne < 1)
            {
                throw new ArgumentException();
            }
            var index = 0;
            var whichEmptyIsIt = 0;
            foreach (var element in pArray)
            {
                if (element == -1)
                {
                    whichEmptyIsIt++;
                    if (whichEmptyIsIt == pWhichOne)
                    {
                        return index;
                    }
                }
                index++;
            }
            return -1;
        }

        static void DrawColumn(int pX, int pY, int pHeight, int pValue)
        {
            for (var i = 0; i < pHeight; i++)
            {
                PermutationsChart[pY + i][pX] = pValue;                
            }
        }

        static long Factorial(int pOfWhat)
        {
            return Subfactorial(2, pOfWhat);
        }

        static long Subfactorial(int pFrom, int pUntil)
        {
            if (pFrom > pUntil)
            {
                throw new ArgumentException();
            }
            var result = 1;
            for (var multiplier = pFrom;
                multiplier <= pUntil;
                multiplier++)
            {
                result *= multiplier;
            }
            return result;
        }

        static void CreateHowTallColumns()
        {
            _howTallColumns = new long[_smallerSetSize];
            _howTallColumns[_smallerSetSize - 1] = 1;
            if (_smallerSetSize >= 2)
            {
                _howTallColumns[_smallerSetSize - 2] = (_biggerSetSize - _smallerSetSize) + 1;
            }
            var factorial = (_biggerSetSize == _smallerSetSize) ? 2 : 3;
            for (var index = _smallerSetSize - 3; index > -1; index--)
            {
                _howTallColumns[index] = Factorial(factorial++);
            }
        }

        internal static void Generate(
            int pBiggerSet,
            int pSmallerSet)
        {
            SetNAndM(pBiggerSet, pSmallerSet);
            CreateHowTallColumns();

            var clearColumns = _biggerSetSize + 1;
            for (var i = 0; i < _smallerSetSize; i++)
            {
                clearColumns--;
                var y = 0;
                var height = Convert.ToInt16(_howTallColumns[i]);
                var stop = false;
                while (!stop)
                {
                    for (var column = 1; column <= clearColumns; column++)
                    {   
                        DrawColumn(
                            GetFreeLaneFromTheLeft(PermutationsChart[y], column),
                            y, height, i);
                        y += height;
                        stop = (y + height) > PermutationsChart.Count;
                        if (stop)
                        {
                            break;
                        }
                    }
                }
            }
        }

        internal static void Print()
        {
            foreach (var array in PermutationsChart)
            {
                foreach (var i in array)
                {
                    Trace.Write(((i >= 0) ? i.ToString() : "-") + " ");
                }
                Trace.Write(Environment.NewLine);
            }
        }

        static long NumberOfPermutations(
            int pBiggerSetSize,
            int pSmallerSetSize)
        {
            return Subfactorial(
                (pBiggerSetSize - pSmallerSetSize) + 1,
                pBiggerSetSize);
        }

        internal static bool TooBig(int pBiggerSetSize, int pSmallerSetSize)
        {
            return (NumberOfPermutations(pBiggerSetSize, pSmallerSetSize) >
                Settings.Default.MaximumAllowedPermutationsForDiscrepancy);
        }

        static void SetNAndM(int pBiggerSetSize, int pSmallerSetSize)
        {
            #region arguments check
            if (pSmallerSetSize > pBiggerSetSize ||
                pSmallerSetSize < 0 ||
                pBiggerSetSize < 0)
            {                
                throw new ArgumentException();                
            }
            if (Subfactorial(pSmallerSetSize, pBiggerSetSize) >
                Settings.Default.MaximumAllowedPermutationsForDiscrepancy)
            {
                throw new TooBigSetsPermutatorException();
            }
            #endregion

            _biggerSetSize = pBiggerSetSize;
            _smallerSetSize = pSmallerSetSize;
            PermutationsChart = new List<int[]>();
            var chartHeight = NumberOfPermutations(_biggerSetSize, _smallerSetSize);
            for (var y = 0; y < chartHeight; y++)
            {
                var row = new int[_biggerSetSize];
                for (var x = 0; x < _biggerSetSize; x++)
                {
                    row[x] = -1;
                }
                PermutationsChart.Add(row);
            }            
        }

    }

    public class TooBigSetsPermutatorException : ApplicationException
    {
    }
}
